package com.healthy_software.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.tea.TeaException;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.healthy_software.api.SmsConfig;
import com.healthy_software.constant.JwtClaimsConstant;
import com.healthy_software.constant.MessageConstant;
import com.healthy_software.dto.*;
import com.healthy_software.entity.User;
import com.healthy_software.exception.LoginFailedException;
import com.healthy_software.mapper.UserMapper;
import com.healthy_software.properties.JwtProperties;
import com.healthy_software.properties.WeChatProperties;
import com.healthy_software.result.Result;
import com.healthy_software.service.UserService;
import com.healthy_software.utils.HttpClientUtil;
import com.healthy_software.utils.JwtUtil;
import com.healthy_software.utils.RegexUtils;
import com.healthy_software.utils.UserIdHolder;
import com.healthy_software.vo.RegisterVO;
import com.healthy_software.vo.UserInfoVO;
import com.healthy_software.vo.UserLoginVO;
import com.healthy_software.vo.UserVO;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.healthy_software.constant.RedisConstants.*;
import static com.healthy_software.constant.SystemConstants.USER_NICK_NAME_PREFIX;

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    //微信服务接口
    public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private JwtProperties jwtProperties;

    @Resource
    private SmsConfig smsConfig;

    @Autowired
    private WeChatProperties weChatProperties;

    @Resource
    private UserMapper userMapper;

    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        //校验手机号还是用户名
        String username = loginForm.getUsername();
        String passwordDTO = loginForm.getPassword();
        User user;
        if (RegexUtils.isPhoneInvalid(username)) {
            // 是用户名
            user = query().eq("nickname", username).one();
            return Result.fail(MessageConstant.PHONE_NUMBER_ERROR);
        }
        else {
            //是手机号
            //根据手机号查询用户 select * from user where phone = ?
            user = query().eq("phone", username).one();
        }
        // 5.判断用户是否存在
        if (user == null) {
            // 6.不存在
            return Result.fail(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        //判断密码是否正确
        passwordDTO = DigestUtil.md5Hex(passwordDTO);
        if(!user.getPassword().equals(passwordDTO)) {
            //不正确
            return Result.fail(MessageConstant.PASSWORD_ERROR);
        }
        // 7.保存用户信息到 redis中,用于后序拦截器认证
        // 7.1.随机生成token，作为登录令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.USER_ID, user.getId());
        claims.put(Claims.EXPIRATION, new Date(System.currentTimeMillis() + jwtProperties.getUserTtl()));
        String token = JwtUtil.createJWT(
                jwtProperties.getUserSecretKey(),
                jwtProperties.getUserTtl(),
                claims);
        //String token = UUID.randomUUID().toString(true);


        // 7.2.将User对象转为HashMap存储
        UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
        Map<String, Object> userMap = BeanUtil.beanToMap(userVO, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName, fieldValue) -> fieldValue == null ? null : fieldValue.toString()));
        // 7.3.存储
        String tokenKey = LOGIN_USER_KEY + token;
        stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);

        // 7.4.设置token有效期
        boolean remenber = loginForm.isRemember();
        if(Boolean.FALSE.equals(remenber)) {
            stringRedisTemplate.expire(tokenKey, NOT_REMEMBER_USER, TimeUnit.HOURS);
        }
        else{
            stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.DAYS);
        }
        //封装返回的 VO 对象
        UserInfoVO userInfoVO = new UserInfoVO();
        userInfoVO.setNickname(user.getNickname());
        userInfoVO.setAvatar(user.getAvatar());
        UserLoginVO userLoginVO = new UserLoginVO();
        userLoginVO.setToken(token);
        userLoginVO.setId(user.getId());
        userLoginVO.setUserInfo(userInfoVO);
        // 8.返回token
        return Result.ok(MessageConstant.LOGIN_SUCCESS,userLoginVO);
    }

    @Override
    public Result sendCode(String phone) {
        //1校验手机号
        if(RegexUtils.isPhoneInvalid(phone)){
            //2不符合返回错误信息
            return Result.fail(MessageConstant.PHONE_NUMBER_ERROR);
        }
        //符合，生成验证码
        String code = RandomUtil.randomNumbers(6);
        com.aliyun.dysmsapi20170525.Client client;
        try {
            client = smsConfig.createClient();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        com.aliyun.dysmsapi20170525.models.SendSmsRequest sendSmsRequest = new com.aliyun.dysmsapi20170525.models.SendSmsRequest()
                .setSignName("阿里云短信测试")
                .setTemplateCode("SMS_317215944")
                .setPhoneNumbers(phone)
                .setTemplateParam("{\"code\":\"" + code + "\"}");
        com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
        try {
            com.aliyun.dysmsapi20170525.models.SendSmsResponse resp = client.sendSmsWithOptions(sendSmsRequest, runtime);
            com.aliyun.teaconsole.Client.log(com.aliyun.teautil.Common.toJSONString(resp));
        } catch (TeaException error) {
            com.aliyun.teautil.Common.assertAsString(error.message);
        } catch (Exception _error) {
            TeaException error = new TeaException(_error.getMessage(), _error);
            com.aliyun.teautil.Common.assertAsString(error.message);
        }

        //保存验证码到redis
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
        //发送验证码
        log.debug("发送验证码成功{}",code);
        //返回ok
        return Result.ok(MessageConstant.CODE_SEND_SUCCESS);
    }

    @Override
    public Result register(RegisterDTO registerDTO) {
        // 1.校验手机号
        String phone = registerDTO.getPhone();
        String password = registerDTO.getPassword();
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 2.如果不符合，返回错误信息
            return Result.fail(MessageConstant.PHONE_NUMBER_ERROR);
        }
        // 3.从redis获取验证码并校验
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        String code = registerDTO.getCode();
        if (cacheCode == null || !cacheCode.equals(code)) {
            // 不一致，报错
            return Result.fail(MessageConstant.CODE_ERROE);
        }
        //一致
        User user = query().eq("phone", phone).one();
        if(user != null) {
            return Result.fail(MessageConstant.ALREADY_EXISTS);
        }
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        user = creatUser(phone, password);
        Long userId = user.getId();

        // 7.保存用户信息到 redis中
        // 7.1.随机生成token，作为登录令牌
        //String token = UUID.randomUUID().toString(true);
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.USER_ID, user.getId());
        claims.put(Claims.EXPIRATION, new Date(System.currentTimeMillis() + jwtProperties.getUserTtl()));
        String token = JwtUtil.createJWT(
                jwtProperties.getUserSecretKey(),
                jwtProperties.getUserTtl(),
                claims);
        // 7.2.将User对象转为HashMap存储
        UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
        Map<String, Object> userMap = BeanUtil.beanToMap(userVO, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName, fieldValue) -> fieldValue == null ? null : fieldValue.toString()));
        // 7.3.存储
        String tokenKey = LOGIN_USER_KEY + token;
        stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
        // 7.4.设置token有效期
        stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);

        RegisterVO registerVO = new RegisterVO();
        registerVO.setUserId(String.valueOf(userId));
        registerVO.setToken(token);
        return Result.ok(MessageConstant.REGISTER_SUCCESS ,registerVO);
    }

    @Override
    public Result resetPassword(ResetPasswordDTO resetPasswordDTO) {
        String phone = resetPasswordDTO.getPhone();
        User user = query().eq("phone", phone).one();
        if(user == null) {
            return Result.fail(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        // 3.从redis获取验证码并校验
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        String code = resetPasswordDTO.getCode();
        if (cacheCode == null || !cacheCode.equals(code)) {
            // 不一致，报错
            return Result.fail(MessageConstant.CODE_ERROE);
        }
        //一致
        //重置密码
        String password = resetPasswordDTO.getNewPassword();
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        user.setPassword(password);
        updateById(user);
        return Result.ok(MessageConstant.RESET_PASSWORD_SUCCESS);
    }

    @Override
    public User wxLogin(UserWXLoginDTO userWXLoginDTO) {
        //调用微信接口服务，获得当前微信用户的openid
        String openId = getOpenId(userWXLoginDTO.getCode());

        //判断openid是否为空，若为空则表示登陆失败，抛出异常
        if(openId == null) {
            throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
        }

        //判断当前用户是否为新用户
        User user = query().eq("openid", openId).one();

        //如果是新用户，自动完成注册
        if(user == null) {
            user = User.builder()
                    .openid(openId)
                    .build();
            userMapper.insert(user);
        }

        //返回这个用户对象
        return user;
    }

    @Override
    public Result setBasicUserInfo(UserVO userVO) {
        Long userId = UserIdHolder.getCurrentId();
        User user = query().eq("id", userId).one();
        if(user == null) {
            return Result.fail(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        user.setNickname(userVO.getNickname());
        user.setGender(userVO.getGender());
        user.setBirthday(userVO.getBirthday());
        user.setWeight(userVO.getWeight());
        user.setHeight(userVO.getHeight());
        updateById(user);
        return Result.ok(MessageConstant.USER_INFO_SET_SUCCESS);
    }

    @Override
    public Result setUserHealthyInfo(UserHealthyInfoDTO userHealthyInfoDTO) {
        Long userId = UserIdHolder.getCurrentId();
        User user = query().eq("id", userId).one();
        if(user == null) {
            return Result.fail(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        user.setActivityLevel(userHealthyInfoDTO.getActivityLevel());
        user.setExerciseBaseLevel(userHealthyInfoDTO.getExerciseBaseLevel());
        user.setKnownHealthIssues(userHealthyInfoDTO.getKnownHealthIssues());
        user.setDietPreference(userHealthyInfoDTO.getDietPreference());
        user.setDietRestrictions(userHealthyInfoDTO.getDietRestrictions());
        user.setSleepQualityRating(userHealthyInfoDTO.getSleepQualityRating());
        user.setStressLevelRating(userHealthyInfoDTO.getStressLevelRating());
        user.setExtraHealthInfo(userHealthyInfoDTO.getExtraHealthInfo());
        user.setUpdatedTime(LocalDateTime.now());
        updateById(user);
        return Result.ok(MessageConstant.USER_HEALTHY_INFO_SET_SUCCESS);
    }

    @Override
    public Result<User> getUserById(Long userId) {
        User user = query().eq("id", userId).one();
        System.out.println(userId);
        return Result.ok(MessageConstant.USER_GET_SUCCESS, user);
    }

    /**
     * 调用微信接口服务来获取微信用户Openid
     * @param code
     * @return
     */
    private String getOpenId(String code) {
        Map<String, String> map = new HashMap<>();
        map.put("appid", weChatProperties.getAppid());
        map.put("secret", weChatProperties.getSecret());
        map.put("js_code", code);
        map.put("grant_type", "authorization_code");
        String json = HttpClientUtil.doGet(WX_LOGIN, map);//包含用户唯一标识等

        JSONObject jsonObject = JSON.parseObject(json);
        String openId = jsonObject.getString("openid");
        return openId;
    }

    private User creatUser(String phone, String password) {
        User user = new User();
        user.setPhone(phone);
        user.setPassword(password);
        user.setNickname(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
        save(user);
        return user;
    }

}
